package edu.stanford.nlp.parser.ensemble.maltparser.parser.history;

import java.util.ArrayList;
import java.util.HashMap;


import edu.stanford.nlp.parser.ensemble.maltparser.core.exception.MaltChainedException;
import edu.stanford.nlp.parser.ensemble.maltparser.core.pool.ObjectPoolList;
import edu.stanford.nlp.parser.ensemble.maltparser.core.symbol.TableHandler;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.action.ActionDecision;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.action.ComplexDecisionAction;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.action.GuideDecision;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.action.GuideUserAction;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.container.ActionContainer;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.container.CombinedTableContainer;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.container.TableContainer;
import edu.stanford.nlp.parser.ensemble.maltparser.parser.history.kbest.KBestList;

/**
*
* @author Johan Hall
* @since 1.1
**/
public class History implements GuideUserHistory, GuideHistory {
	protected final ObjectPoolList<ComplexDecisionAction> actionPool;
	protected Class<? extends KBestList> kBestListClass = null;
	protected int kBestSize;
	protected String separator = "~";
	protected String decisionSettings;
	protected ArrayList<TableContainer> decisionTables;
	protected ArrayList<TableContainer> actionTables; 
	protected HashMap<String, TableHandler> tableHandlers;
	
	public History(String decisionSettings, String separator, HashMap<String, TableHandler> tableHandlers) throws MaltChainedException {
		setTableHandlers(tableHandlers);
		setSeparator(separator);
		initDecisionSettings(decisionSettings);
		actionPool = new ObjectPoolList<ComplexDecisionAction>() {
			protected ComplexDecisionAction create() throws MaltChainedException { return new ComplexDecisionAction(getThis()); }
			public void resetObject(ComplexDecisionAction o) throws MaltChainedException { o.clear(); }
		};
		clear();
	}
	
	private History getThis() {
		return this;
	}
	
	/* GuideUserHistory interface */
	public GuideUserAction getEmptyGuideUserAction() throws MaltChainedException {
		return (GuideUserAction)getEmptyActionObject();
	}
	
	public ArrayList<ActionContainer> getActionContainers() {
		ArrayList<ActionContainer> actionContainers = new ArrayList<ActionContainer>();
		for (int i=0; i<actionTables.size(); i++) {
			actionContainers.add(new ActionContainer(actionTables.get(i)));
		}
		return actionContainers;
	}
	
	public ActionContainer[] getActionContainerArray() {
		ActionContainer[] actionContainers = new ActionContainer[actionTables.size()];
		for (int i=0; i<actionTables.size(); i++) {
			actionContainers[i] = new ActionContainer(actionTables.get(i));
		}
		return actionContainers;
	}
	
	
	public void clear() throws MaltChainedException {
		actionPool.checkInAll();
	}
	
//	public void clear() {
//		currentAction = -1;
//	}
	
	/* GuideHistory interface */
	public GuideDecision getEmptyGuideDecision() throws MaltChainedException {
		return (GuideDecision)getEmptyActionObject();
	}
	
	public int getNumberOfDecisions() {
		return decisionTables.size();
	}
	
	public TableHandler getTableHandler(String name) {
		return tableHandlers.get(name);
	}

	public Class<? extends KBestList> getKBestListClass() {
		return kBestListClass;
	}
	
	public void setKBestListClass(Class<?> kBestListClass) throws MaltChainedException {
		try {
			if (kBestListClass != null) {
				this.kBestListClass = kBestListClass.asSubclass(edu.stanford.nlp.parser.ensemble.maltparser.parser.history.kbest.KBestList.class);
			}
		} catch (ClassCastException e) {
			throw new HistoryException("The class '"+kBestListClass.getName()+"' is not a subclass of '"+edu.stanford.nlp.parser.ensemble.maltparser.parser.history.kbest.KBestList.class.getName()+"'. ", e);
		}
	}
	
	public int getKBestSize() {
		return kBestSize;
	}

	public void setKBestSize(int kBestSize) {
		this.kBestSize = kBestSize;
	}

	public int getNumberOfActions() {
		return actionTables.size();
	}
	
	public ArrayList<TableContainer> getDecisionTables() {
		return decisionTables;
	}

	public ArrayList<TableContainer> getActionTables() {
		return actionTables;
	}

	public HashMap<String, TableHandler> getTableHandlers() {
		return tableHandlers;
	}
	
	public String getSeparator() {
		return separator;
	}

	public void setSeparator(String separator) throws MaltChainedException {
		if (separator == null || separator.length() < 1) {
			throw new HistoryException("The class item separator (--guide-classitem_separator) does not have correct value. ");
		}
		this.separator = separator;
	}

	public String getDecisionSettings() {
		return decisionSettings;
	}

	public void setDecisionSettings(String decisionSettings) {
		this.decisionSettings = decisionSettings;
	}

	protected void setTableHandlers(HashMap<String, TableHandler> tableHandlers) {
		this.tableHandlers = tableHandlers;
	}
	
	protected ActionDecision getEmptyActionObject() throws MaltChainedException {
		return actionPool.checkOut();
	}
	
	protected void initDecisionSettings(String decisionSettings) throws MaltChainedException {
		decisionTables = new ArrayList<TableContainer>();
		actionTables = new ArrayList<TableContainer>();
		this.decisionSettings = decisionSettings;
		int start = 0;
		int k = 0;
		char prevDecisionSeparator = ' ';
		TableContainer tmp = null;
		final StringBuilder sbTableHandler = new StringBuilder();
		final StringBuilder sbTable = new StringBuilder();
		int state = 0;
		for (int i = 0; i < decisionSettings.length(); i++) {
			switch (decisionSettings.charAt(i)) {
			case '.':
				if (state != 0) {
					//error
				}
				state = 1;
				break;
			case '+':
				tmp = new TableContainer(tableHandlers.get(sbTableHandler.toString()).getSymbolTable(sbTable.toString()), sbTableHandler.toString()+"."+sbTable.toString(), '+');
				actionTables.add(tmp);
				k++;
				sbTableHandler.setLength(0);
				sbTable.setLength(0);
				state = 0;
				break;
			case '#':
				state = 2;
				break;
			case ';':
				state = 2;
				break;
			case ',':
				state = 2;
				break;
			default:
				if (state == 0) {
					sbTableHandler.append(decisionSettings.charAt(i));
				} else if (state == 1) {
					sbTable.append(decisionSettings.charAt(i));
				}
			}
			if (state == 2 || i == decisionSettings.length()-1) {
				char decisionSeparator = decisionSettings.charAt(i);
				if (i == decisionSettings.length()-1) {
					//decisionSeparator = ' ';
					decisionSeparator = prevDecisionSeparator;
				}
				tmp = new TableContainer(tableHandlers.get(sbTableHandler.toString()).getSymbolTable(sbTable.toString()), sbTableHandler.toString()+"."+sbTable.toString(), decisionSeparator);
				actionTables.add(tmp);
				k++;
				if (k-start > 1) {
					decisionTables.add(new CombinedTableContainer(getTableHandler("A"), separator, actionTables.subList(start, k), decisionSeparator));
				} else {
					decisionTables.add(tmp);
				}
				sbTableHandler.setLength(0);
				sbTable.setLength(0);
				state = 0;
				start = k;
				prevDecisionSeparator = decisionSeparator;
			}
		}
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		return sb.toString();
	}
}
